﻿using Loong.Threading.Locks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using RedLockNet;
using RedLockNet.SERedis;
using RedLockNet.SERedis.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Loong.Redis.Threading.Locks
{
    public class RedisLockFactory : LockFactoryBase
    {
        private readonly Dictionary<string, IRedLock> _redLocks;
        private readonly Lazy<RedLockFactory> _redLockFactory;

        private readonly LockOptions _lockOptions;
        private readonly RedisManager _redisManager;

        public RedisLockFactory(
            ILogger<RedisLockFactory> logger,
            IOptionsMonitor<LockOptions> options,
            RedisManager redisManager)
            : base(logger, options)
        {
            _redLocks = new Dictionary<string, IRedLock>();
            _redLockFactory = new Lazy<RedLockFactory>(CreateRedLockFactory);

            _lockOptions = options.CurrentValue;
            _redisManager = redisManager;
        }

        private RedLockFactory CreateRedLockFactory()
        {
            var endPoints = _redisManager.GetConnection().GetEndPoints()
                .Select(e => new RedLockEndPoint { EndPoint = e })
                .ToList();

            return RedLockFactory.Create(endPoints);
        }

        protected override async Task AcquireAsync(string resource, string owner, TimeSpan expiryTime)
        {
            var redLock = await _redLockFactory.Value.CreateLockAsync(resource, expiryTime, expiryTime, _lockOptions.DefaultRetryTime);

            if (!redLock.IsAcquired)
            {
                throw new LockTimeoutException(resource);
            }

            _redLocks.Add(BuildKey(resource, owner), redLock);
        }

        protected override void Release(string resource, string owner)
        {
            var key = BuildKey(resource, owner);
            _redLocks[key]?.Dispose();
            _redLocks.Remove(key);
        }
    }
}
